<template>
    <div style="margin-bottom: 20px; border-bottom: 1px solid #ddd;padding-bottom: 5px;">
        <label v-if="label" :class="{'i-form-item-label-required': isRequired}">{{label}}</label>
        <div>
            <slot></slot>
            <div v-if="validateState === 'error'" class="i-form-item-message">{{validateMessage}}</div>
        </div>
    </div>
</template>

<script>
    import Emitter from '../../mixins/emitter.js'
    import AsyncValidator from 'async-validator'

    export default {
        name: 'iFormItem',
        mixins: [Emitter],
        inject: ['form'],
        provide() {
            return {
                formItem: this
            }
        },
        props: {
            label: {
                type: String,
                default: ''
            },
            prop: {
                type: String
            }
        },
        data() {
            return {
                isRequired: false, // 是否为必填
                validateState: '', // 检验状态
                validateMessage: '' // 校验不通过的提示信息
            }
        },

        // 从form的model中获取当前表单组件的数据
        computed: {
            fieldValue() {
                return this.form.model[this.prop]
            }
        },

        // 组件渲染时，将实例缓存在form里
        mounted() {
            // 如果没有传入prop，则无需校验，也无需缓存
            // 需要注意，组件渲染顺序是从内到外，内部mounted时，外面的form还没mounted
            // 所以需要在form  created时监听事件
            // form的created要先于formItem的mounted
            if (this.prop) {
                this.dispatch('iForm', 'on-form-item-add', this)

                // 设置初始值，以便在重置时恢复默认数值
                this.initialValue = this.fieldValue
                this.setRules()
            }
        },

        methods: {
            // 从form获取当前表单的规则
            getRules() {
                let formRules = this.form.rules

                formRules = formRules ? formRules[this.prop] : []
                return [].concat(formRules || [])
            },

            // 只支持blur和change，过滤出符合要求的规则
            getFilteredRule(trigger) {
                const rules = this.getRules()
                // 这里为啥返回 !rule.trigger???
                // 知道了，这里是判断先有trigger
                // 我自己对这个判断改一下，有trigger且含有，然后再返回
                return rules.filter(rule => rule.trigger && rule.trigger.indexOf(trigger) !== -1)
            },

            /**
             * 校验数据
             * @param trigger 校验类型
             * @param callback 回调函数
             */
            validate(trigger, callback) {
                let rules = this.getFilteredRule(trigger)

                if (!rules || rules.length === 0) {
                    return true // 不需要检验 直接true
                }

                this.validateState = 'validating'

                // 以下为validate库的方法
                const validator = new AsyncValidator({ [this.prop]: rules })

                let model = {}
                model[this.prop] = this.fieldValue
                validator.validate(model, { firstFields: true }, errors => {
                    this.validateState = !errors ? 'success' : 'error'
                    this.validateMessage = errors ? errors[0].message : ''
                    callback(this.validateMessage)
                })

            },

            setRules() {
                let rules = this.getRules()
                if (rules.length) {
                    rules.forEach(rule => {
                        // 如果当前校验有必填项，就标记出来
                        if (rule.required) this.isRequired = true
                    })
                }

                this.$on('on-form-change', this.onFieldChange)
                this.$on('on-form-blur', this.onFieldBlur)
            },
            onFieldChange() {
                this.validate('change')
            },
            onFieldBlur() {
                this.validate('blur')
            },

            resetField() {
                this.validateState = ''
                this.validateMessage = ''

                this.form.model[this.prop] = this.initialValue // 重新设置
            }
        },

        // 组件销毁前，将组件从form缓存中移除
        beforeDestroy() {
            if (this.prop) {
                this.dispatch('iForm', 'on-form-item-remove', this)
            }
        }
    }
</script>

<style rel="stylesheet/less" scoped>
    .i-form-item-label-required::before {
        content: '*';
        color: red;
    }

    .i-form-item-message {
        color: red;
    }
</style>
